#include "Pipe.h"

Pipe::Pipe(wchar_t *nameuuid)
{
	m_pipe = INVALID_HANDLE_VALUE;
	wcscpy_s(m_pipename, nameuuid);
}

Pipe::~Pipe()
{
	DisconnectNamedPipe(m_pipe);
	CloseHandle(m_pipe);
	m_pipe = INVALID_HANDLE_VALUE;
	m_isConnect = false;
}

bool Pipe::isConnect()
{
	return (m_pipe != INVALID_HANDLE_VALUE);
}

int Pipe::accept()
{
	HANDLE pipe;

	//OutputDebugString(L"Pipe pipe name ");
	if (m_pipe == INVALID_HANDLE_VALUE) {
		pipe = CreateNamedPipe(
			m_pipename,                // pipe name
			PIPE_ACCESS_DUPLEX,  // read-write access
			PIPE_TYPE_BYTE |     // byte-oriented writes
			PIPE_READMODE_BYTE | // byte-oriented reads
			PIPE_WAIT,           // blocking operations
			PIPE_UNLIMITED_INSTANCES, // no limit on clients
			1024 * 1024,                // input buffer size
			1024 * 1024,                // output buffer size
			0,                   // client time-out
			NULL);               // default security attributes

		if (pipe == INVALID_HANDLE_VALUE) {
			//OutputDebugString("Pipe", "created namedpipe failed err=%d", (int)GetLastError());
			return -1;
		}
	}
	else {
		pipe = m_pipe;
	}
	// Stupid Win32 API design: If a client is already connected, then
	// ConnectNamedPipe will return 0, and GetLastError() will return
	// ERROR_PIPE_CONNECTED. This is not an error! It just means that the
	// function didn't have to wait.
	//
	if (::ConnectNamedPipe(pipe, NULL) == 0 && GetLastError() != ERROR_PIPE_CONNECTED) {
		//OutputDebugString("Pipe", "  failed err=%d", (int)GetLastError());
		//ERR("%s: ConnectNamedPipe failed: %d\n", __FUNCTION__, (int)GetLastError());
		CloseHandle(pipe);
		return -1;
	}
	m_pipe = pipe;
	m_isConnect = true;
	return 0;
}

const unsigned char *Pipe::readFully(void *buf, size_t len)
{
	if (m_pipe == INVALID_HANDLE_VALUE)
	{
		m_isConnect = false;
		return NULL;
	}

	if (!buf) {
		return NULL;  // do not allow NULL buf in that implementation
	}
	size_t res = len;
	while (res > 0) {
		DWORD  readcount = 0;
		if (!::ReadFile(m_pipe, (char *)buf + (len - res), res, &readcount, NULL) || readcount == 0) {
			errno = (int)GetLastError();
			return NULL;
		}
		res -= readcount;
	}
	return (const unsigned char *)buf;
}

const unsigned char *Pipe::read(void *buf, size_t *inout_len)
{
	size_t len = *inout_len;
	DWORD  readcount;

	if (m_pipe == INVALID_HANDLE_VALUE)
	{
		m_isConnect = false;
		return NULL;
	}

	if (!buf) {
		return NULL;  // do not allow NULL buf in that implementation
	}

	if (!::ReadFile(m_pipe, (char *)buf, len, &readcount, NULL)) {
		errno = (int)GetLastError();
		return NULL;
	}

	*inout_len = (size_t)readcount;
	return (const unsigned char *)buf;
}

int Pipe::write(void *buf, size_t size)
{
	if (m_pipe == INVALID_HANDLE_VALUE)
	{
		m_isConnect = false;
		return -1;
	}

	size_t res = size;
	int retval = 0;

	while (res > 0) {
		DWORD  written = 0;
		if (!WriteFile(m_pipe, (const char *)buf + (size - res), res, &written, NULL)) {
			retval = -1;
			//ERR("%s: failed: %d\n", __FUNCTION__, (int)GetLastError());
			break;
		}
		res -= written;
	}
	return retval;
}

void Pipe::close()
{
	FlushFileBuffers(m_pipe);
	DisconnectNamedPipe(m_pipe);
	CloseHandle(m_pipe);
	m_pipe = INVALID_HANDLE_VALUE;
	m_isConnect = false;
}